package biz

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"server/base"
	"strconv"
	"strings"

	"github.com/360EntSecGroup-Skylar/excelize"
)

type GameRaw struct {
	Id         int
	Name       string
	Platform   string
	No         string
	Photo      string
	Aka        string
	Genre      string
	En         string
	Des        string
	Players    int
	Vr         bool
	ReleasedAt int
	Tags       string
}

type VersionRaw struct {
	Id     int
	GameId int
	No     string
	Name   string
	Des    string
}

type PackageRaw struct {
	Id          int
	No          string
	GameId      int
	VersionId   int
	Name        string
	Type        int
	TitleId     string
	ContentId   string
	Sysv        string
	Ver         float64
	Country     string
	Lang        string
	Sizea       float64
	Src         string
	NewFileName string // 重命名为新的文件名
	BindId      int
}

type Importer struct {
	platform string
	pndups   map[string]int // 用于生成包名序号 {PName: 重复次数}

	pkglist []*PackageRaw // 最终解析结果的包列表
}

func NewImporter() *Importer {
	o := new(Importer)
	o.pndups = make(map[string]int)
	return o
}

// system firmware to sysv_ids
func (this *Importer) ToSysv(firmware string) []int {
	if firmware == "NA" {
		firmware = "0"
	}
	var out []int
	return out
}

func (this *Importer) makePkgName(verno, ver, category string) string {
	ver = strings.TrimLeft(ver, "0")
	if ver == "NA" {
		ver = ""
	}
	vf, _ := strconv.ParseFloat(ver, 64)
	typecn := ""
	switch category {
	case "1":
		typecn = "本体"
	case "2":
		typecn = "升级补丁"
	case "3":
		typecn = "DLC"
	case "4":
		typecn = "MOD"
	case "5":
		typecn = "金手指"
	case "6":
		typecn = "PS2本体"
	case "7":
		typecn = "模拟器"
	case "8":
		typecn = "降级补丁"
	case "9":
		typecn = "免登录补丁"
	}
	var pn string
	if vf != 0 {
		pn = fmt.Sprintf("%.02f%s", vf, typecn)
	} else {
		pn = typecn
	}
	k := fmt.Sprintf("%s-%s", verno, pn)
	this.pndups[k]++
	no := this.pndups[k]
	if no <= 1 {
		return pn
	}
	return fmt.Sprintf("%s-%d", pn, no)
}

type ImportRow struct {
	A       string // game no
	H       string // version no
	I       string // version name
	M       string // lang
	Q       string // region
	S       string // version (NA=empty)
	Version float64
}

func (this *Importer) ParseRow(row []string) *ImportRow {
	r := &ImportRow{
		A: row[0],
		H: row[7],
		I: row[8],
		M: row[12],
		Q: row[16],
		S: row[18],
	}
	r.S = strings.TrimLeft(r.S, "0")
	var err error
	r.Version, err = strconv.ParseFloat(r.S, 64)
	if err != nil {
		r.Version = 0.0
	}
	return r
}

// 区域的中文名
func (this *Importer) RegionCN(region string) string {
	switch region {
	case "EU":
		return "欧版"
	case "HONG KONG":
		return "港版"
	case "JAPAN":
		return "日版"
	case "US":
		return "美版"
	}
	return region
}

// 语言中文
func (this *Importer) LangCN(lang string) string {
	switch lang {
	case "cn":
		return "中文"
	case "en":
		return "英文"
	case "jp":
		return "日文"
	}
	return lang
}

// 生成版本名PL[max]R[max]
func (this *Importer) mkVname(rows [][]string, i int) string {
	main := this.ParseRow(rows[i]) // 主包
	n := len(rows)
	maxv := main.Version
	hascn := false
	for i = i + 1; i < n; i++ {
		r := this.ParseRow(rows[i])
		if r.A != "" && r.A != main.A || r.H != "" && r.H != main.H {
			break
		}
		if r.Version > main.Version {
			maxv = r.Version
		}
		if strings.ToLower(r.M) == "cn" {
			hascn = true
		}
	}
	if hascn {
		main.M = "cn"
	}
	//v := strconv.FormatFloat(maxv, 'f', -1, 64)
	return fmt.Sprintf("%s%s%.02f", this.RegionCN(main.Q), this.LangCN(main.M), maxv)
}

// 保留n位小数
func (this *Importer) toFixed(f string, n int) string {
	doti := strings.IndexAny(f, ".")
	if doti < 0 {
		return f
	}
	if doti+n < len(f) {
		return f[:doti+n+1]
	}
	return f
}

func (this *Importer) Read(r io.Reader) error {
	f, err := excelize.OpenReader(r)
	if err != nil {
		return err
	}
	rows := f.GetRows("Sheet1")
	rows = rows[1:]
	// trim space
	for _, row := range rows {
		for i := range row {
			row[i] = strings.TrimSpace(row[i])
		}
	}
	var lastgame *GameRaw
	var lastver *VersionRaw
	var dupnames = make(map[string]string) // {src: pkg.name}
	var dupnos = make(map[string]string)   // {no: pkg.Name}
	var warn string
	for i, row := range rows {
		if len(row) < 26 {
			continue
		}
		A := row[0]
		B := row[1]
		C := row[2]
		D := row[3]
		E := row[4]
		F := row[5]
		G := row[6]
		H := row[7]
		I := row[8]
		J := row[9]
		K := row[10]
		L := row[11]
		M := row[12]
		N := row[13]
		O := row[14]
		P := row[15]
		Q := row[16]
		R := row[17]
		S := row[18]
		//T := row[19]
		U := row[20]
		V := row[21]
		W := row[22]
		//X := row[23]
		//Y := row[24]
		Z := row[25]

		// game
		if A != "" && B != "" {
			no := A
			genre := strings.ReplaceAll(E, "|", ",")
			players, _ := strconv.Atoi(F)
			released_at, _ := base.FromDate(G)
			newg := &GameRaw{
				No:         no,
				Name:       B,
				Aka:        C,
				En:         D,
				Genre:      genre,
				Players:    players,
				Vr:         W == "Yes",
				ReleasedAt: released_at,
			}
			newg.Photo = fmt.Sprintf("save/%s/ICON_S.png", newg.En)
			lastgame = newg
			lastver = nil
		}
		// version
		if H != "" && lastgame != nil {
			no := fmt.Sprintf("%s-%s", lastgame.No, H)
			newv := &VersionRaw{
				GameId: lastgame.Id,
				No:     no,
				Name:   I,
				Des:    J,
			}
			if newv.Name == "" {
				newv.Name = this.mkVname(rows, i)
				//fmt.Println("vname:", newv.Name)
			}
			lastver = newv
		}
		S = strings.TrimLeft(S, "0")
		if S == "NA" {
			S = ""
		}
		S = this.toFixed(S, 2)
		// package
		if L == "" && lastver != nil {
			L = this.makePkgName(lastver.No, S, U)
		}
		if K != "" && L != "" && N != "" && P != "" && lastver != nil {
			no := fmt.Sprintf("%s-%s", lastver.No, K)
			sysv := this.ToSysv(R)
			src := Z + "\\" + N
			size, _ := strconv.ParseFloat(V, 64)
			typ, _ := strconv.Atoi(U)
			p := &PackageRaw{
				GameId:      lastver.GameId,
				VersionId:   lastver.Id,
				No:          no,
				Name:        L,
				Type:        typ,
				Lang:        M,
				Src:         src,
				TitleId:     O,
				ContentId:   P,
				Country:     Q,
				Sysv:        base.JoinInts(sysv),
				Sizea:       size,
				NewFileName: fmt.Sprintf("%s-%s-%s-%s.pkg", lastgame.No, lastgame.Name, lastver.Name, L),
			}
			p.Ver, _ = strconv.ParseFloat(S, 64)
			k := src
			if name, ok := dupnames[k]; ok {
				log.Println("pkg src重复:", k, name)
				warn += fmt.Sprintln("pkg src重复:", k, name, "<br>")
				continue
			}
			dupnames[k] = p.Name
			if name, ok := dupnos[no]; ok {
				log.Println("pkg no重复:", no, name)
				warn += fmt.Sprintln("pkg no重复:", no, name, "<br>")
				continue
			}
			dupnos[no] = p.Name
			this.pkglist = append(this.pkglist, p)
		}
		_ = lastver
	}
	fmt.Println("pkglist:", len(this.pkglist), len(dupnames))
	if warn != "" {
		return errors.New(warn)
	}
	return nil
}

func (this *Importer) Pkglist() []*PackageRaw {
	return this.pkglist
}

// 检测excel与本地文件是否缺失
func CheckExcel(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	im := NewImporter()
	err = im.Read(f)
	if err != nil {
		return err
	}
	for _, p := range im.Pkglist() {
		if !base.FileExists(p.Src) {
			return fmt.Errorf("包文件缺失: %s", base.JsonString(p))
		}
	}
	return nil
}
